package jeff.plugin.mybatis.util;

import jeff.plugin.mybatis.exception.UtilException;
import jeff.plugin.mybatis.lang.Charsets;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * 字符串工具类
 */
public class StringUtil {

    public static String format(String template, Object... objects){
        return substitute(template, objects);
    }

    public static String substitute(String template, Object... objects){
        return substituteWithMacro(template, "{", "}", objects);
    }

    public static String substituteWithMacro(final String template, final String macroStart, final String macroEnd, Object... objects){
        StringBuilder result = new StringBuilder(template.length());

        final char escapeChar = '\\';

        final int startLen = macroStart.length();
        final int endLen = macroEnd.length();
        final int objectsLen = objects.length;

        int i = 0;
        int len = template.length();
        int replaceNum = -1;
        while (i < len) {
            int ndx = template.indexOf(macroStart, i);
            if (ndx == -1 || replaceNum >= objectsLen - 1) {
                result.append(i == 0 ? template : template.substring(i));
                break;
            }

            // check escaped
            int j = ndx - 1;
            boolean escape = false;
            int count = 0;

            while ((j >= 0) && (template.charAt(j) == escapeChar)) {
                escape = !escape;
                if (escape) {
                    count++;
                }
                j--;
            }
            result.append(template.substring(i, ndx));

            if (escape) {
                result.append(macroStart);
                i = ndx + startLen;
                continue;
            }

            int ndx1 = ndx;
            int ndx2 = template.indexOf(macroEnd, ndx);
            if(ndx2 == ndx1 + 1){
                replaceNum++;
                result.append(objects[replaceNum]);
                i = ndx2 + 1;
            }
            else{
                result.append(template.substring(ndx1,ndx1+1));
                i = ndx1 + 1;
            }
        }

        return result.toString();
    }

    public static String str(byte[] data) {
        return str(data, Charsets.defaultCharset());
    }

    /**
     * 解码字节码
     *
     * @param data 字符串
     * @param charset 字符集，如果此字段为空，则解码的结果取决于平台
     * @return 解码后的字符串
     */
    public static String str(byte[] data, Charset charset) {
        if (data == null) {
            return null;
        }

        if (null == charset) {
            return new String(data);
        }
        return new String(data, charset);
    }

    /**
     * 使用系统编码来解码字节码
     * @param data 字符串
     * @return 解码后的字符串
     */
    public static String str(Byte[] data) {
        return str(data, Charsets.defaultCharset());
    }

    /**
     * 解码字节码
     *
     * @param data 字符串
     * @param charset 字符集，如果此字段为空，则解码的结果取决于平台
     * @return 解码后的字符串
     */
    public static String str(Byte[] data, Charset charset) {
        if (data == null) {
            return null;
        }

        byte[] bytes = new byte[data.length];
        Byte dataByte;
        for (int i = 0; i < data.length; i++) {
            dataByte = data[i];
            bytes[i] = (null == dataByte) ? -1 : dataByte;
        }

        return str(bytes, charset);
    }

    /**
     * 将编码的byteBuffer数据转换为字符串
     *
     * @param data 数据
     * @param charset 字符集，如果为空使用当前系统字符集
     * @return 字符串
     */
    public static String str(ByteBuffer data, Charset charset) {
        if (null == charset) {
            charset = Charset.defaultCharset();
        }
        return charset.decode(data).toString();
    }

    /**
     * {@link CharSequence} 转为字符串，null安全
     *
     * @param cs {@link CharSequence}
     * @return 字符串
     */
    public static String str(CharSequence cs) {
        return null == cs ? null : cs.toString();
    }

    public static String str(Object obj, Charset charset) {
        if (null == obj) {
            return null;
        }

        if (obj instanceof String) {
            return (String) obj;
        } else if (obj instanceof byte[]) {
            return str((byte[]) obj, charset);
        } else if (obj instanceof Byte[]) {
            return str((Byte[]) obj, charset);
        } else if (obj instanceof ByteBuffer) {
            return str((ByteBuffer) obj, charset);
        }
        return obj.toString();
    }

    public static String str(Object obj) {
        return str(obj, Charsets.defaultCharset());
    }


    public static String newString(final byte[] bytes) {
        try {
            return new String(bytes,"UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new UtilException(e);
        }
    }
    public static String newString(final byte[] bytes, final String charsetName) {
        try {
            return new String(bytes, charsetName);
        } catch (UnsupportedEncodingException e) {
            throw new UtilException(e);
        }
    }

    public static byte[] getBytes(final String string) {
        try {
            return string.getBytes("utf-8");
        } catch (UnsupportedEncodingException e) {
            throw new UtilException(e);
        }
    }

    public static byte[] getBytes(final String string, final Charset charset) {
        return getBytes(string, Charsets.name(charset));
    }

    public static byte[] getBytes(final String string, final String charsetName) {
        try {
            return string.getBytes(charsetName);
        } catch (UnsupportedEncodingException e) {
            throw new UtilException(e);
        }
    }
    public static boolean hasLength(String str) {
        return (str != null && str.length() > 0);
    }


    public static boolean hasText(String str) {
        if (!hasLength(str)) {
            return false;
        }
        int strLen = str.length();
        for (int i = 0; i < strLen; i++) {
            if (!Character.isWhitespace(str.charAt(i))) {
                return true;
            }
        }
        return false;
    }

    public static boolean isBlank(CharSequence str) {
        int strLen;
        if (str == null || (strLen = str.length()) == 0) {
            return true;
        }
        for (int i = 0; i < strLen; i++) {
            if ( !Character.isWhitespace(str.charAt(i)) ) {
                return false;
            }
        }
        return true;
    }
}
